﻿using CG5_KT.Primitives;
using System;

namespace CG5_KT.Scene
{
    public class Camera
    {
        public enum CameraMode
        {
            Free,
            TPP,
            Nose
        }

        public CameraMode Mode { get; set; }
        public Vector3 Position { get; set; }
        public Vector3 Target { get; set; }
        public Vector3 FollowedObjectPosition { get; set; }

        private float freeTranslationStep = 0.1f;
        private float tppAngleStep = (float)0.02;

        public Camera()
        {
            this.Mode = CameraMode.TPP;
        }

        public void Strafe(Vector3 translation)
        {
            this.Position = this.Position + translation;
            this.Target = this.Target + translation;
        }

        public void YawAndPitch(Vector3 angleXYZ)
        {
            var viewVect = this.Position - this.FollowedObjectPosition;
            viewVect = Vector3.Transform(viewVect, new Quaternion(angleXYZ, 1));

            this.Position = this.FollowedObjectPosition + viewVect;
            this.Target = this.FollowedObjectPosition;
        }

        public void GoUp()
        {
            if (this.Mode == CameraMode.Free)
            {
                this.Strafe(new Vector3(0, this.freeTranslationStep, 0));
            }
            else if (this.Mode == CameraMode.TPP)
            {
                this.YawAndPitch(new Vector3(this.tppAngleStep, 0, 0));
            }
        }

        public void GoDown()
        {
            if (this.Mode == CameraMode.Free)
            {
                this.Strafe(new Vector3(0, -this.freeTranslationStep, 0));
            }
            else if (this.Mode == CameraMode.TPP)
            {
                this.YawAndPitch(new Vector3(-this.tppAngleStep, 0, 0));
            }
        }

        public void GoLeft()
        {
            if (this.Mode == CameraMode.Free)
            {
                this.Strafe(new Vector3(-this.freeTranslationStep, 0, 0));
            }
            else if (this.Mode == CameraMode.TPP)
            {
                this.YawAndPitch(new Vector3(0, -this.tppAngleStep, 0));
            }
        }

        public void GoRight()
        {
            if (this.Mode == CameraMode.Free)
            {
                this.Strafe(new Vector3(this.freeTranslationStep, 0, 0));
            }
            else if (this.Mode == CameraMode.TPP)
            {
                this.YawAndPitch(new Vector3(0, this.tppAngleStep, 0));
            }
        }

        public void GoForward()
        {
            if (this.Mode == CameraMode.Free)
            {
                this.Strafe(new Vector3(0, 0, -this.freeTranslationStep));
            }
            else if (this.Mode == CameraMode.TPP)
            {
                var viewVectStep = CalcTppForwardStrafe();
                this.Strafe(viewVectStep);
            }
        }

        public void GoBackward()
        {
            if (this.Mode == CameraMode.Free)
            {
                this.Strafe(new Vector3(0, 0, this.freeTranslationStep));
            }
            else if (this.Mode == CameraMode.TPP)
            {
                var viewVectStep = CalcTppForwardStrafe();
                this.Strafe(new Vector3(
                    viewVectStep.X * -1,
                    viewVectStep.Y * -1,
                    viewVectStep.Z * -1));
            }
        }

        private Vector3 CalcTppForwardStrafe()
        {
            var vDirection = this.FollowedObjectPosition - this.Position;
            var len = vDirection.Length();
            float ratio = this.freeTranslationStep / len;
            return new Vector3(vDirection.X * ratio, vDirection.Y * ratio, vDirection.Z * ratio);
        }
    }
}